# 无人机巡检航线规划[200分]
# 题目内容
某电力公司使用无人机对 $n$ 个电力塔进行巡检。每个电力塔 $i$ 位于坐标 $(x_i, y_i)$,无人机从基地(坐标 $(0,0)$)出发,需要依次飞达每个电力塔完成巡检后结束任务,无需返回基地。
无人机同一时刻只能飞向一个电力塔,请规划巡检顺序,使无人机飞行的总距离最短(两个坐标点间按曼哈顿距离计算,即 $|x_1-x_2| + |y_1-y_2|$),输出最短总距离。
# 输入描述
- $n$:电力塔数量,$1 \le n \le 15$
- $x_i, y_i$:第 $i$ 个电力塔的坐标,$0 \le x_i, y_i \le 200$
# 输出描述
输出最短总距离(整数)。
# 样例
# 样例 1
输入
3
1 2
3 1
2 3
1
2
3
4
2
3
4
输出
8
1
说明: 3 个电力塔:$A(1,2)$、$B(3,1)$、$C(2,3)$。基地为 $O(0,0)$。
- 巡检顺序 $A \to C \to B$:$O \to A$ 距离 $|1-0|+|2-0|=3$,$A \to C$ 距离 $|1-2|+|2-3|=2$,$C \to B$ 距离 $|2-3|+|3-1|=3$。总距离 $3+2+3=8$
- 巡检顺序 $A \to B \to C$:$O \to A$ 距离 3,$A \to B$ 距离 $|1-3|+|2-1|=3$,$B \to C$ 距离 $|3-2|+|1-3|=3$。总距离 9
- 巡检顺序 $B \to A \to C$:$O \to B$ 距离 4,$B \to A$ 距离 3,$A \to C$ 距离 2。总距离 9
其余顺序总距离均不小于 8,最短总距离为 8。
# 样例 2
输入
2
1 1
2 2
1
2
3
2
3
输出
4
1
说明: 2 个电力塔:$A(1,1)$、$B(2,2)$。基地为 $O(0,0)$。
- 巡检顺序 $A \to B$:$O \to A$ 距离 $|0-1|+|0-1|=2$,$A \to B$ 距离 $|1-2|+|1-2|=2$。总距离 $2+2=4$
- 巡检顺序 $B \to A$:$O \to B$ 距离 $|0-2|+|0-2|=4$,$B \to A$ 距离 $|2-1|+|2-1|=2$。总距离 $4+2=6$
最短总距离为 4。
# 代码
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
let inputs = [];
rl.on('line', (input) => {
inputs.push(input.split(' ').map(Number));
})
rl.on('close', () => {
const n = inputs.shift()[0];
const arr = inputs;
// ========== 优化1:预计算距离矩阵 ==========
// d[i][j] 表示塔 i 到塔 j 的曼哈顿距离
const d = Array.from({length: n}, () => Array(n).fill(0));
for(let i = 0; i < n; i++) {
for(let j = 0; j < n; j++) {
d[i][j] = Math.abs(arr[i][0] - arr[j][0]) + Math.abs(arr[i][1] - arr[j][1]);
}
}
const used = new Array(n).fill(false); // 标记哪些塔已访问
let count = 0; // 已访问数量,替代 filter
const memo = new Map(); // 记忆化缓存
// ========== 优化2:dfs 返回"剩余距离" ==========
// dfs(c) 表示:当前站在塔 c,飞完所有还没访问的塔,最少还要飞多远
const dfs = (c) => {
// 全部飞完了,剩余距离为 0
if(count === n) return 0;
// 用"当前位置 + 当前访问状态"作为缓存 key
// 例如:"2,true,false,true" 表示在2号塔,0号和2号已访问
const key = c + ',' + used.join(',');
if(memo.has(key)) return memo.get(key);
let minExtra = Infinity;
for(let i = 0; i < n; i++) {
if(!used[i]) {
used[i] = true; // 去 i 号塔
count++; // 访问数 +1
// 从 c 飞到 i 的距离 + 从 i 继续飞完剩下的距离
const dist = d[c][i] + dfs(i);
if(dist < minExtra) minExtra = dist;
count--; // 回溯:恢复访问数
used[i] = false; // 回溯:恢复状态
}
}
memo.set(key, minExtra);
return minExtra;
}
let ans = Infinity;
for(let i = 0; i < n; i++) {
used[i] = true;
count = 1;
// 总距离 = 基地(0,0)到 i 的距离 + 从 i 飞完剩余的距离
const total = (arr[i][0] + arr[i][1]) + dfs(i);
if(total < ans) ans = total;
used[i] = false;
count = 0;
}
console.log(ans);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73